package my.edu.model.bills.sales.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import my.edu.common.constant.MessageConstant;
import my.edu.common.constant.OrderConstant;
import my.edu.common.domain.OrderProductDetail;
import my.edu.common.domain.PageDTO;
import my.edu.common.domain.PageVO;
import my.edu.common.domain.purchase.PriceAndQuantity;
import my.edu.common.exception.BillsException;
import my.edu.common.exception.Order.SalesException;
import my.edu.common.utils.PageUtils;
import my.edu.constant.BillsType;
import my.edu.constant.OrderNoConstant;
import my.edu.model.bills.sales.entity.SalesOrder;
import my.edu.model.bills.sales.entity.SalesOrderDetail;
import my.edu.model.bills.sales.entity.SalesReturnOrderDetail;
import my.edu.model.bills.sales.entity.dto.SalesReturnDTO;
import my.edu.model.bills.sales.entity.dto.SalesReturnDetailDTO;
import my.edu.model.bills.sales.entity.dto.SalesReturnOrderSearchDTO;
import my.edu.model.bills.sales.entity.vo.SalesDetailVO;
import my.edu.model.bills.sales.entity.vo.SalesReturnDetailVO;
import my.edu.model.bills.sales.entity.vo.SalesReturnVO;
import my.edu.model.bills.sales.entity.vo.SalesVO;
import my.edu.model.commodity.service.IProductService;
import my.edu.model.information.service.IAccountService;
import my.edu.utils.ChildrenLoader;
import my.edu.utils.OrderNoUtil;
import my.edu.utils.OrderServiceUtil;
import my.edu.websocket.BillWebSocket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import my.edu.model.bills.sales.entity.SalesReturnOrder;
import my.edu.model.bills.sales.mapper.SalesReturnOrderMapper;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

@Service
public class SalesReturnOrderServiceImpl extends ServiceImpl<SalesReturnOrderMapper, SalesReturnOrder> implements ISalesReturnOrderService {

    private record SalesReturnDTOResult(
            PriceAndQuantity priceAndQuantity,
            List<SalesReturnDetailDTO> salesReturnDetailDTOList,
            String returnStatus
    ) {
    }

    @Autowired
    private ISalesReturnOrderDetailService salesReturnOrderDetailService;
    @Autowired
    private ISalesOrderService salesOrderService;
    @Autowired
    private OrderServiceUtil orderServiceUtil;
    @Autowired
    private OrderNoConstant orderNoConstant;
    @Autowired
    private IProductService productService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private BillWebSocket billWebSocket;

    @Override
    @Transactional
    public void saveNew(SalesReturnDTO salesReturnDTO) {
        SalesReturnDTOResult dtoResult = getDTOResult(salesReturnDTO);
        SalesReturnOrder salesReturnOrder = setOrderField(BeanUtil.copyProperties(salesReturnDTO, SalesReturnOrder.class, "salesReturnDetailDTOList"), dtoResult, true);
        save(salesReturnOrder);
        salesReturnOrderDetailService.saveBatchByDTO(dtoResult.salesReturnDetailDTOList, salesReturnOrder.getId());
        if (salesReturnDTO.getSalesOrderId() != null && salesReturnDTO.getSalesOrderId() > 0) {
            salesOrderService.updateReturnId(salesReturnOrder,dtoResult.returnStatus);
        }
        billWebSocket.sendToAllClient(BillsType.SALESRETURN,"单据类型：销售退货单;单据编号："+salesReturnOrder.getOrderNo()+";提交人："+salesReturnOrder.getCreatedName());

    }

    private SalesReturnOrder setOrderField(SalesReturnOrder salesReturnOrder, SalesReturnDTOResult dtoResult, boolean isNew) {
        List<SalesReturnDetailDTO> salesReturnDetailDTOS = dtoResult.salesReturnDetailDTOList();
        PriceAndQuantity priceAndQuantity = dtoResult.priceAndQuantity;
        salesReturnOrder.setQuantity(priceAndQuantity.getQuantity());
        salesReturnOrder.setReSalesAmount(priceAndQuantity.getAmount());
        if (salesReturnOrder.getOrderNo() == null || salesReturnOrder.getOrderNo().isEmpty()) {
            //设置No
            String orderNo = OrderNoUtil.generateOrderNo(orderNoConstant.getSalesReturnNo());
            salesReturnOrder.setOrderNo(orderNo);
        } else {
            //检查是否有重复no
            long count = count(Wrappers.<SalesReturnOrder>lambdaQuery().eq(SalesReturnOrder::getOrderNo, salesReturnOrder.getOrderNo()));
            if (isNew && count > 0) throw new BillsException(MessageConstant.ORDER_NO_EXISTS);
        }
        //设置优惠后金额
        BigDecimal amount = salesReturnOrder.getReSalesAmount().subtract(salesReturnOrder.getDiscountAmount());
        if (amount.compareTo(BigDecimal.ZERO) < 0) {
            throw new SalesException(MessageConstant.AMOUNT_ERROR);
        }
        salesReturnOrder.setAmountAfterDiscount(amount);//优惠后金额
        //设置欠款
        BigDecimal debt = amount.subtract(salesReturnOrder.getReReceivedAmount());
        salesReturnOrder.setDebt(debt);
        //设置付款状态
        setPaidStatusAndDebt(salesReturnOrder);
      salesReturnOrder.setIsAudited(0);
        return salesReturnOrder;
    }

    private void setPaidStatusAndDebt(SalesReturnOrder salesReturnOrder) {
        BigDecimal amountAfterDiscount = salesReturnOrder.getAmountAfterDiscount();
        String status;
        BigDecimal paidAmount = salesReturnOrder.getReReceivedAmount();
        if (amountAfterDiscount.compareTo(BigDecimal.ZERO) != 0 && amountAfterDiscount.compareTo(paidAmount) != 0) {
            //没有付一分钱
            if (paidAmount.compareTo(BigDecimal.ZERO) == 0)
                status = OrderConstant.NOT_REPAYMENT;
            else if (amountAfterDiscount.compareTo(paidAmount) > 0)
                status = OrderConstant.PART_REPAYMENT;
            else throw new BillsException(MessageConstant.PAID_AMOUNT_ERROR);
        } else {
            status = OrderConstant.GET_REPAYMENT;
        }
        salesReturnOrder.setRefundStatus(status);
    }


    private SalesReturnDTOResult getDTOResult(SalesReturnDTO salesReturnDTO) {
        List<SalesReturnDetailDTO> salesReturnDetailDTOList = salesReturnDTO.getSalesReturnDetailDTOList();
        Map<String, Object> map = Map.of();
        if (salesReturnDTO.getSalesOrderId() != null && salesReturnDTO.getSalesOrderId() > 0) {
            map = salesOrderService.checkSalesOrderAndSetCache(salesReturnDTO);
            salesReturnDTO.setSourcePurchaseQuantity((Integer) map.get("quantity"));
            salesReturnDTO.setSalesOrderNo((String) map.get("orderNo"));
        }
        orderServiceUtil.checkDTO(salesReturnDTO.getDeliveryMethod(), salesReturnDTO.getSettlementAccountId(), salesReturnDTO.getSalespersonId(), null, salesReturnDTO.getCustomerId());
        PriceAndQuantity priceAndQuantity = orderServiceUtil.checkAndGetProductDetail(OrderConstant.IGNORE_PRICE, salesReturnDetailDTOList);
        return new SalesReturnDTOResult(priceAndQuantity, salesReturnDetailDTOList, (String) map.get("returnStatus"));
    }

    @Override
    @Transactional
    public void removeOrder(List<Long> ids) {
        List<SalesReturnOrder> salesReturnOrders = listByIds(ids);
        for (SalesReturnOrder salesReturnOrder : salesReturnOrders) {
            if (salesReturnOrder.getIsAudited() != null && salesReturnOrder.getIsAudited() == 1)
                throw new BillsException(salesReturnOrder.getOrderNo() + MessageConstant
                        .ISAUDIT_CAN_NOT_DEL);
        }
        removeBatchByIds(salesReturnOrders);
        salesReturnOrderDetailService.remove(Wrappers.<SalesReturnOrderDetail>lambdaQuery().in(SalesReturnOrderDetail::getSalesReturnOrderId, ids));
        salesOrderService.removeReturn(ids);
    }

    @Override
    @Transactional
    public void updateOrder(SalesReturnDTO salesReturnDTO) {
        //存在性和有无审核校验
        SalesReturnOrder salesReturnOrder = getById(salesReturnDTO.getId());
        Long salesOrderId = salesReturnOrder.getSalesOrderId();
        if (salesReturnOrder == null) throw new BillsException(MessageConstant.ORDER_NOT_EXIST);
        if (salesReturnOrder.getIsAudited() != null && salesReturnOrder.getIsAudited() == 1)
            throw new BillsException(MessageConstant.ISAUDIT_CAN_NOT_UPDATE);

        SalesReturnDTOResult dtoResult = getDTOResult(salesReturnDTO);
        BeanUtil.copyProperties(salesReturnDTO, salesReturnOrder, "salesReturnDetailDTOList");
        setOrderField(salesReturnOrder, dtoResult, false);
        updateById(salesReturnOrder);
        salesReturnOrderDetailService.updateBatchByDTO(dtoResult.salesReturnDetailDTOList, salesReturnOrder.getId());
        salesOrderService.updateReturnId(salesOrderId, salesReturnOrder,dtoResult.returnStatus);
        billWebSocket.sendToAllClient(BillsType.SALESRETURN,"更新;单据类型：销售退货单;单据编号："+salesReturnOrder.getOrderNo()+";提交人："+salesReturnOrder.getUpdateName());

    }

    @Override
    public void auditOrder(List<Long> ids) {
        List<SalesReturnOrder> salesReturnOrders = checkOrderAudit(ids, true);
        orderServiceUtil.setAuditStatus(this, ids, true);
        changeStock(ids, true);
        //TODO，更新账户金额
        accountService.addBalance(orderServiceUtil.getAccountIdToTotalAmountMap(salesReturnOrders));
    }

    private void changeStock(List<Long> ids, boolean isAdd) {
        List<OrderProductDetail> orderProductDetails = salesReturnOrderDetailService
                .list(Wrappers.<SalesReturnOrderDetail>lambdaQuery()
                        .in(SalesReturnOrderDetail::getSalesReturnOrderId, ids))
                .stream().map(i -> (OrderProductDetail) i).toList();
        if (isAdd) {
            productService.addCurrentStock(orderProductDetails);
        } else {
            productService.subCurrentStock(orderProductDetails);
        }
    }

    @Override
    public void rauditOrder(List<Long> ids) {
        List<SalesReturnOrder> salesReturnOrders = checkOrderAudit(ids, false);
        orderServiceUtil.setAuditStatus(this, ids, false);
        changeStock(ids, false);
        accountService.subBalance(orderServiceUtil.getAccountIdToTotalAmountMap(salesReturnOrders));
    }

    private List<SalesReturnOrder> checkOrderAudit(List<Long> ids, boolean isAudit) {
        List<SalesReturnOrder> salesReturnOrders = listByIds(ids);
        if (salesReturnOrders.isEmpty()) throw new BillsException(MessageConstant.ORDER_NOT_EXIST);
        for (SalesReturnOrder salesReturnOrder : salesReturnOrders) {
            if (salesReturnOrder.getIsAudited() != null && salesReturnOrder.getIsAudited() == 1&&isAudit)
                throw new BillsException(salesReturnOrder.getOrderNo() + MessageConstant.ISAUDITED);
            else if ((salesReturnOrder.getIsAudited() == null || salesReturnOrder.getIsAudited() == 0)&&!isAudit)
                throw new BillsException(salesReturnOrder.getOrderNo() + MessageConstant.NOTAUDITED);
        }

        return salesReturnOrders;
    }

    @Override
    public PageVO<SalesReturnVO> getSalesOrder(PageDTO page, SalesReturnOrderSearchDTO salesReturnOrderSearchDTO) {
        Page<SalesReturnVO> objectPage = new Page<>(page.getPage(), page.getSize());
        MPJLambdaWrapper<SalesReturnOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(SalesReturnOrder.class, SalesReturnVO.class)
                .like(salesReturnOrderSearchDTO.getOrderNo() != null && !salesReturnOrderSearchDTO.getOrderNo().isEmpty(),
                        SalesReturnOrder::getOrderNo, salesReturnOrderSearchDTO.getOrderNo())
                .between(salesReturnOrderSearchDTO.getBeginTime() != null,
                        SalesReturnOrder::getOrderDate, salesReturnOrderSearchDTO.getBeginTime(), salesReturnOrderSearchDTO.getEndTime())
                .eq(salesReturnOrderSearchDTO.getDeliveryMethod() != null,
                        SalesReturnOrder::getDeliveryMethod, salesReturnOrderSearchDTO.getDeliveryMethod())
                .eq(salesReturnOrderSearchDTO.getReturnStatus() != null && !salesReturnOrderSearchDTO.getReturnStatus().isEmpty(),
                        SalesReturnOrder::getRefundStatus, salesReturnOrderSearchDTO.getReturnStatus())
                .eq(salesReturnOrderSearchDTO.getIsAudited() != null,
                        SalesReturnOrder::getIsAudited, salesReturnOrderSearchDTO.getIsAudited())
                .eq(salesReturnOrderSearchDTO.getSalespersonId() != null,
                        SalesReturnOrder::getSalespersonId, salesReturnOrderSearchDTO.getSalespersonId())
                .eq(salesReturnOrderSearchDTO.getSalesOrderId() != null,
                        SalesReturnOrder::getSalesOrderId, salesReturnOrderSearchDTO.getSalesOrderId())
                .eq(salesReturnOrderSearchDTO.getAuditorId() != null,
                        SalesReturnOrder::getAuditorId, salesReturnOrderSearchDTO.getAuditorId())
                .eq(salesReturnOrderSearchDTO.getCustomerId() != null,
                        SalesReturnOrder::getCustomerId, salesReturnOrderSearchDTO.getCustomerId())
                .orderByDesc(SalesReturnOrder::getCreateTime);
        Page<SalesReturnVO> salesReturnVOPage = baseMapper.selectJoinPage(objectPage, SalesReturnVO.class, wrapper);
//        List<CompletableFuture<Void>> futures = new ArrayList<>();
//        for (SalesReturnVO vo : salesOrderPage.getRecords()) {
//            futures.add(CompletableFuture.runAsync(() -> setVoDetail(vo)));
//        }
//        // 等待所有异步任务完成
//        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        if (CollUtil.isNotEmpty(salesReturnVOPage.getRecords())) {
            ChildrenLoader.loadAndFillDTO(
                    salesReturnVOPage.getRecords(),
                    SalesReturnVO::getId,
                    ids -> salesReturnOrderDetailService.list(
                            Wrappers.<SalesReturnOrderDetail>lambdaQuery().in(SalesReturnOrderDetail::getSalesReturnOrderId, ids)),
                    SalesReturnDetailVO::getSalesReturnOrderId,
                    SalesReturnDetailVO.class,
                    SalesReturnVO::setSalesReturnDetailVOList);
        };
        return PageUtils.toPage(salesReturnVOPage);
    }

//    @Async
//    protected void setVoDetail(SalesReturnVO vo) {
//        vo.setSalesReturnDetailVOList(
//                BeanUtil.copyToList(
//                        salesReturnOrderDetailService.list(Wrappers.<SalesReturnOrderDetail>lambdaQuery().eq(SalesReturnOrderDetail::getSalesReturnOrderId, vo.getId()))
//                        , SalesReturnDetailVO.class
//                )
//        );
//    }

    @Override
    public SalesReturnVO getSalesOrderOne(Long id) {
        MPJLambdaWrapper<SalesReturnOrder> wrapper = new MPJLambdaWrapper<>();
        wrapper
                .selectAsClass(SalesReturnOrder.class, SalesReturnVO.class)
                .selectCollection(SalesReturnOrderDetail.class, SalesReturnVO::getSalesReturnDetailVOList)
                .leftJoin(SalesReturnOrderDetail.class, SalesReturnOrderDetail::getSalesReturnOrderId, SalesReturnOrder::getId)
                .eq(SalesReturnOrder::getId, id);
        return baseMapper.selectJoinOne(SalesReturnVO.class, wrapper);
    }

    @Override
    public List<SalesReturnVO> listOnly(SalesReturnOrderSearchDTO salesReturnOrderSearchDTO) {
        return list(Wrappers.<SalesReturnOrder>lambdaQuery()
                .like(salesReturnOrderSearchDTO.getOrderNo() != null && !salesReturnOrderSearchDTO.getOrderNo().isEmpty(),
                        SalesReturnOrder::getOrderNo, salesReturnOrderSearchDTO.getOrderNo())
                .between(salesReturnOrderSearchDTO.getBeginTime() != null,
                        SalesReturnOrder::getOrderDate, salesReturnOrderSearchDTO.getBeginTime(), salesReturnOrderSearchDTO.getEndTime())
                .eq(salesReturnOrderSearchDTO.getReturnStatus() != null && !salesReturnOrderSearchDTO.getReturnStatus().isEmpty(),
                        SalesReturnOrder::getRefundStatus, salesReturnOrderSearchDTO.getReturnStatus())
                .eq(salesReturnOrderSearchDTO.getIsAudited() != null,
                        SalesReturnOrder::getIsAudited, salesReturnOrderSearchDTO.getIsAudited())
                .eq(salesReturnOrderSearchDTO.getSalespersonId() != null,
                        SalesReturnOrder::getSalespersonId, salesReturnOrderSearchDTO.getSalespersonId())
                .eq(salesReturnOrderSearchDTO.getAuditorId() != null,
                        SalesReturnOrder::getAuditorId, salesReturnOrderSearchDTO.getAuditorId())
                .eq(salesReturnOrderSearchDTO.getCustomerId() != null,
                        SalesReturnOrder::getCustomerId, salesReturnOrderSearchDTO.getCustomerId())
                .orderByDesc(SalesReturnOrder::getCreateTime)).stream().map(i -> BeanUtil.copyProperties(i, SalesReturnVO.class)).toList();
    }

    @Override
    public SalesReturnVO getOrderOne(Long id) {
        return getSalesOrderOne(id);
    }
}
